const fs = require('fs');
const path = require('path');
const { whenProd, whenDev } = require('@craco/craco')
const request = require('sync-request');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackTagsPlugin = require('html-webpack-tags-plugin');
const { targetName, dependencies } = require('./package.json');

module.exports = {
  webpack: {
    configure: (webpackConfig, { env, paths }) => {
      paths.appBuild = 'dist';

      webpackConfig.output = {
        ...webpackConfig.output,
        ...{
          library: targetName,
          libraryTarget: 'umd',

          path: path.resolve(paths.appPath, 'dist'),
          filename: targetName + '/js/[name].[contenthash:8].js',
          chunkFilename: targetName + '/js/[name].[contenthash:8].chunk.js',
          assetModuleFilename: targetName + '/media/[name].[hash:8][ext]',
        }
      };
      webpackConfig.plugins = webpackConfig.plugins.map((plugin) => {
        whenProd(() => {
          if (plugin instanceof MiniCssExtractPlugin) {
            Object.assign(plugin.options, {
              filename: targetName + '/css/[name].[contenthash:8].css',
              chunkFilename: targetName + '/css/[name].[contenthash:8].chunk.css',
            });
          }
        });
        return plugin;
      });

      whenDev(() => {
        // 动态添加依赖库的预加载
        const { host } = require('./server.config.js');
        const res = request('GET', `${host}${paths.publicUrlOrPath}entrypoints.json`);
        const entrypoints = JSON.parse(res.getBody().toString());
        const local_depts = getLibmanDependencies(paths.appPath);
        if (local_depts.length) {
          webpackConfig.plugins.push(new HtmlWebpackTagsPlugin({
            tags: local_depts,
            append: false
          }));
        }
        const depts = getNpmDependencies(paths.appPath);
        Object.values(depts).forEach(name => {
          if (entrypoints[name]) {
            webpackConfig.plugins.push(new HtmlWebpackTagsPlugin({
              tags: entrypoints[name],
              append: false
            }));
          }
        });
      });

      webpackConfig.module.rules = webpackConfig.module.rules.map((rule) => {
        if (rule.oneOf) {
          rule.oneOf = rule.oneOf.map((oneOfRule) => {
            if (oneOfRule.options && oneOfRule.options.name) {
              oneOfRule.options.name = targetName + '/media/[name].[hash:8].[ext]';
            }
            if (oneOfRule.use) {
              if (Array.isArray(oneOfRule.use)) {
                oneOfRule.use.forEach(use => {
                  if (use.options && use.options.name) {
                    use.options.name = targetName + '/media/[name].[hash:8].[ext]';
                  }
                });
              } else {
                if (oneOfRule.use.options && oneOfRule.use.options.name) {
                  oneOfRule.use.options.name = targetName + '/media/[name].[hash:8].[ext]';
                }
              }
            }
            return oneOfRule;
          });
        }
        return rule;
      });

      webpackConfig.externals = {
        ...webpackConfig.externals,
        'react': 'React',
        'react-dom': 'ReactDOM',
        'dayjs': 'dayjs',
        'antd': 'antd',
        'vue': 'Vue',
        // 动态排除依赖库的打包
        ...getNpmDependencies(paths.appPath),
      };

      return webpackConfig;
    },
  },
  devServer: (devServerConfig, { env, paths, proxy, allowedHost }) => {
    const { host } = require('./server.config.js');
    devServerConfig.proxy = {
      '/': {
        target: host || 'http://localhost:5000',
        secure: false
      },
    };
    return devServerConfig;
  },
};

function getNpmDependencies(appPath) {
  const depts = {};
  // npm依赖
  if (dependencies) {
    try {
      Object.keys(dependencies).forEach(packageName => {
        const txt = fs.readFileSync(path.join(appPath, 'node_modules', packageName, 'package.json'), { encoding: "utf-8" });
        const { name, targetName } = JSON.parse(txt);
        if (name && targetName) {
          depts[name] = targetName;
        }
      });
    } catch (e) {
      if (e.code !== 'ENOENT') throw e;
    }
  }
  return depts;
}

function getLibmanDependencies(appPath) {
  const depts = [];
  const libman_txt = fs.readFileSync(path.join(appPath, 'libman.json')).toString('utf8');
  const libman = JSON.parse(libman_txt);
  libman.libraries.forEach(lib => {
    const files = getAllFiles(path.join(appPath, lib.destination));
    files.forEach(file => {
      const fullname = path.join(lib.library, file);
      if (lib.entrypoints && fullname.toLocaleLowerCase().endsWith('.js')) {
        depts.push(targetName + '/' + fullname);
      }
    });
  });
  return depts;
}

function getAllFiles(basepath) {
  const files = [];
  recursiver(basepath, (_, fullname) => {
    files.push(path.relative(basepath, fullname));
  });
  return files;
}

function recursiver(folder_path, fn) {
  const dirents = fs.readdirSync(folder_path, { withFileTypes: true });
  for (let i = 0; i < dirents.length; i++) {
    const dirent = dirents[i];
    if (dirent.name.startsWith('.')) continue;
    if (dirent.isDirectory()) {
      recursiver(path.join(folder_path, dirent.name), fn);
    } else {
      fn(dirent.name, path.join(folder_path, dirent.name));
    }
  }
};